import pygame
import random
import math
import sys


class Fly:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.size = random.randint(20, 40)
        self.speed_x = random.uniform(-2, 2)
        self.speed_y = random.uniform(-2, 2)
        self.color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        self.alive = True

    def update(self):
        if not self.alive:
            return

        # Move the fly
        self.x += self.speed_x
        self.y += self.speed_y

        # Bounce off edges
        if self.x <= self.size or self.x >= 800 - self.size:
            self.speed_x *= -1
        if self.y <= self.size or self.y >= 600 - self.size:
            self.speed_y *= -1

        # Keep within bounds
        self.x = max(self.size, min(800 - self.size, self.x))
        self.y = max(self.size, min(600 - self.size, self.y))

    def draw(self, screen):
        if not self.alive:
            return

        # Draw fly body
        pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.size)

        # Draw fly eyes
        eye_size = self.size // 3
        pygame.draw.circle(screen, (0, 0, 0), (int(self.x - self.size // 2), int(self.y - self.size // 3)), eye_size)
        pygame.draw.circle(screen, (0, 0, 0), (int(self.x + self.size // 2), int(self.y - self.size // 3)), eye_size)

        # Draw fly wings
        wing_color = (self.color[0] // 2, self.color[1] // 2, self.color[2] // 2)
        pygame.draw.ellipse(screen, wing_color, (self.x - self.size, self.y, self.size, self.size // 2))
        pygame.draw.ellipse(screen, wing_color, (self.x, self.y, self.size, self.size // 2))

    def check_hit(self, x, y):
        # Check if the fly was hit by a shot
        distance = math.sqrt((self.x - x) ** 2 + (self.y - y) ** 2)
        if distance <= self.size and self.alive:
            self.alive = False
            return True
        return False


class Crosshair:
    def __init__(self):
        self.x = 400
        self.y = 300
        self.size = 20

    def update(self, mouse_x, mouse_y):
        self.x = mouse_x
        self.y = mouse_y

    def draw(self, screen):
        # Draw crosshair
        pygame.draw.line(screen, (255, 0, 0), (self.x - self.size, self.y), (self.x + self.size, self.y), 2)
        pygame.draw.line(screen, (255, 0, 0), (self.x, self.y - self.size), (self.x, self.y + self.size), 2)
        pygame.draw.circle(screen, (255, 0, 0), (int(self.x), int(self.y)), 5, 2)


class Game:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((800, 600))
        pygame.display.set_caption("Fly Shooter Game")
        self.clock = pygame.time.Clock()

        # Game variables
        self.flies = []
        self.score = 0
        self.misses = 0
        self.font = pygame.font.Font(None, 36)
        self.small_font = pygame.font.Font(None, 24)

        # Create crosshair
        self.crosshair = Crosshair()

        # Fly spawn timer
        self.spawn_timer = 0
        self.spawn_delay = 60  # frames

        # Game state
        self.game_over = False
        self.max_misses = 10

    def spawn_fly(self):
        # Spawn a fly at a random position
        margin = 50
        x = random.randint(margin, 800 - margin)
        y = random.randint(margin, 600 - margin)
        self.flies.append(Fly(x, y))

    def update(self):
        if self.game_over:
            return

        # Update crosshair with mouse position
        mouse_x, mouse_y = pygame.mouse.get_pos()
        self.crosshair.update(mouse_x, mouse_y)

        # Spawn flies
        self.spawn_timer += 1
        if self.spawn_timer >= self.spawn_delay:
            self.spawn_fly()
            self.spawn_timer = 0
            # Gradually increase difficulty
            self.spawn_delay = max(20, self.spawn_delay - 0.1)

        # Update flies
        for fly in self.flies:
            fly.update()

        # Remove dead flies
        self.flies = [fly for fly in self.flies if fly.alive or self.is_near_screen(fly)]

    def is_near_screen(self, fly):
        # Keep flies that are near the screen for visual effect
        return -50 < fly.x < 850 and -50 < fly.y < 650

    def handle_shot(self, x, y):
        # Check if any fly was hit
        hit = False
        for fly in self.flies:
            if fly.check_hit(x, y):
                self.score += 10
                hit = True
                break

        if not hit:
            self.misses += 1
            if self.misses >= self.max_misses:
                self.game_over = True

    def draw(self):
        # Clear screen
        self.screen.fill((135, 206, 235))  # Sky blue background

        # Draw flies
        for fly in self.flies:
            fly.draw(self.screen)

        # Draw crosshair
        self.crosshair.draw(self.screen)

        # Draw HUD
        score_text = self.font.render(f"Score: {self.score}", True, (0, 0, 0))
        misses_text = self.small_font.render(f"Misses: {self.misses}/{self.max_misses}", True, (0, 0, 0))
        self.screen.blit(score_text, (10, 10))
        self.screen.blit(misses_text, (10, 50))

        # Draw instructions
        instructions = [
            "Left-click to shoot flies",
            "Don't let too many flies escape"
        ]
        for i, instruction in enumerate(instructions):
            text = self.small_font.render(instruction, True, (0, 0, 0))
            self.screen.blit(text, (800 - 300, 10 + i * 25))

        # Draw game over screen
        if self.game_over:
            overlay = pygame.Surface((800, 600))
            overlay.set_alpha(128)
            overlay.fill((0, 0, 0))
            self.screen.blit(overlay, (0, 0))

            game_over_text = self.font.render("Game Over", True, (255, 255, 255))
            final_score_text = self.font.render(f"Final Score: {self.score}", True, (255, 255, 255))
            restart_text = self.small_font.render("Press R to Restart or ESC to Quit", True, (255, 255, 255))

            self.screen.blit(game_over_text, (350, 250))
            self.screen.blit(final_score_text, (330, 300))
            self.screen.blit(restart_text, (270, 350))

    def reset_game(self):
        self.flies = []
        self.score = 0
        self.misses = 0
        self.spawn_timer = 0
        self.spawn_delay = 60
        self.game_over = False

    def run(self):
        running = True

        while running:
            # Handle events
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1 and not self.game_over:  # Left mouse button
                        mouse_x, mouse_y = pygame.mouse.get_pos()
                        self.handle_shot(mouse_x, mouse_y)
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_r and self.game_over:
                        self.reset_game()
                    elif event.key == pygame.K_ESCAPE:
                        running = False

            # Update game state
            self.update()

            # Draw everything
            self.draw()

            # Update display
            pygame.display.flip()
            self.clock.tick(60)

        pygame.quit()
        sys.exit()


if __name__ == "__main__":
    game = Game()
    game.run()